use std::sync::Arc;

use anyhow::bail;
use anyhow::Result;
use config::Config;
use config::FileFormat;
use config::Value;

use crate::default::DEFAULT_CACHE_LOCATION;
use crate::default::DEFAULT_DISTROVERPKG;
use crate::default::DEFAULT_PLUGIN_CONF_PATH;
use crate::default::DEFAULT_PLUGIN_PATH;
use crate::default::DEFAULT_REPO_LOCATION;
use crate::utils::check_dir;
use crate::utils::get_os_arch;

use super::parse_string_to_vec;

#[derive(Debug, Clone)]
pub struct ConfigMain {
    pub installonly_limit: u64,
    pub clean_requirements_on_remove: bool,
    pub gpgcheck: bool,
    pub keepcache: bool,
    pub repodir: String,
    pub cachedir: String,
    pub distroverpkg: String,
    pub excludepkgs: Option<Vec<String>>,
    pub minversions: Option<Vec<String>>,
    pub proxy: Option<String>,
    pub proxy_username: Option<String>,
    pub proxy_password: Option<String>,
    pub plugins: bool,
    pub pluginconfpath: String,
    pub pluginpath: String,
}
impl Default for ConfigMain {
    fn default() -> Self {
        Self {
            installonly_limit: 1,
            clean_requirements_on_remove: false,
            gpgcheck: false,
            keepcache: false,
            repodir: DEFAULT_REPO_LOCATION.to_string(),
            cachedir: DEFAULT_CACHE_LOCATION.to_string(),
            distroverpkg: DEFAULT_DISTROVERPKG.to_string(),
            excludepkgs: None,
            minversions: None,
            proxy: None,
            proxy_username: None,
            proxy_password: None,
            plugins: false,
            pluginconfpath: DEFAULT_PLUGIN_CONF_PATH.to_string(),
            pluginpath: DEFAULT_PLUGIN_PATH.to_string(),
        }
    }
}
impl ConfigMain {
    fn update(&mut self, k: &str, v: Value) -> Result<()> {
        match k {
            "installonly_limit" => {
                self.installonly_limit = v.into_uint()?;
            }
            "clean_requirements_on_remove" => {
                self.clean_requirements_on_remove = v.into_bool()?;
            }
            "gpgcheck" => {
                self.gpgcheck = v.into_bool()?;
            }
            "keepcache" => {
                self.keepcache = v.into_bool()?;
            }

            "repodir" => {
                self.repodir = v.into_string()?.trim_end_matches("/").to_owned() + "/";
            }
            "cachedir" => {
                self.cachedir = v.into_string()?.trim_end_matches("/").to_owned() + "/";
            }
            "distroverpkg" => {
                self.distroverpkg = v.into_string()?;
            }
            "excludepkgs" => {
                self.excludepkgs = parse_string_to_vec(v.into_string()?.as_str());
            }
            "minversions" => {
                self.minversions = parse_string_to_vec(v.into_string()?.as_str());
            }
            "proxy" => {
                self.proxy = Some(v.into_string()?);
            }
            "proxy_username" => {
                self.proxy_username = Some(v.into_string()?);
            }
            "proxy_password" => {
                self.proxy_password = Some(v.into_string()?);
            }
            "plugins" => {
                self.plugins = v.into_bool()?;
            }
            "pluginconfpath" => {
                self.pluginconfpath = v.into_string()?;
            }
            "pluginpath" => {
                self.pluginpath = v.into_string()?;
            }
            _ => {}
        }
        Ok(())
    }
}
#[derive(Debug, Clone)]
pub struct RdnfConfig {
    pub config_main: Arc<ConfigMain>,
    pub var_release_ver: String,
    pub var_base_arch: String,
}
impl RdnfConfig {
    pub fn read(config_file: &str) -> Result<Self> {
        let source = config::File::new(config_file, FileFormat::Ini);
        match Config::builder().add_source(source).build() {
            Ok(conf) => {
                let main = conf.get_table("main")?;
                let mut conf = ConfigMain::default();
                for (k, v) in main {
                    conf.update(k.as_str(), v)?;
                }
                // conf.read_from_HashMap(main)?;
                if !check_dir(conf.repodir.as_str())? {
                    bail!("Dir repodir {} don't have file", conf.repodir.as_str());
                };
                return Ok(Self {
                    config_main: Arc::new(conf),
                    var_release_ver: get_release()?,
                    var_base_arch: get_os_arch()?,
                });
            }
            Err(e) => {
                bail!("Failed to read conf file {} ; because {:?}", config_file, e);
            }
        }
    }
}

fn get_release() -> Result<String> {
    Ok(String::from("37"))
}
#[cfg(test)]
mod tests {
    use crate::conf::parse_string_to_vec;

    // use super::RdnfConfig;

    // const CONF_PATH: &str = "assest/dnf.conf";
    #[test]
    fn test_parse_string_to_vec() {
        let s = "pkga , pkgb ; \t \n pkgc ,pkg-d";
        assert_eq!(
            parse_string_to_vec(s),
            Some(vec![
                "pkga".to_string(),
                "pkgb".to_string(),
                "pkgc".to_string(),
                "pkg-d".to_string()
            ])
        );
    }
}
